<template>
    <div>
        <h4>- Proxy：在目标对象之前架设一层“拦截”，外界对该对象的访问，都必须先通过这层拦截</h4>
        <code>var proxy = new Proxy(target, handler);</code>
        <p>Proxy 实例也可以作为其他对象的原型对象；对于可以设置、但没有设置拦截的操作，则直接落在目标对象上，按照原先的方式产生结果</p>
        <ul>
            <b>- Proxy 支持的拦截操作：</b>
            <li><b>get(target, propKey, receiver)</b> 拦截某个属性的读取操作，可以接受三个参数，依次为目标对象、属性名和 proxy 实例本身（严格地说，是操作行为所针对的对象），其中最后一个参数可选；可以继承；如果一个属性不可配置（configurable）且不可写（writable），则 Proxy 不能修改该属性，否则通过 Proxy 对象访问该属性会报错。</li>
            <li><b>set(target, propKey, value, receiver)</b> 拦截某个属性的赋值操作，可以接受四个参数，依次为目标对象、属性名、属性值和 Proxy 实例本身，其中最后一个参数可选，返回一个布尔值；如果目标对象自身的某个属性，不可写且不可配置，那么set方法将不起作用。</li>
            <li><b>apply(target, object, args)</b> 拦截函数的调用、call、apply和Reflect.apply方法操作，可以接受三个参数，分别是目标对象、目标对象的上下文对象（this）和目标对象的参数数组；</li>
            <li><b>has(target, propKey) </b>拦截HasProperty操作，即判断对象是否具有某个属性时，这个方法会生效。典型的操作就是in运算符（只对in运算符生效，对for...in循环不生效）。接受两个参数，分别是目标对象、需查询的属性名 返回布尔值；如果原对象不可配置或者禁止扩展，这时has拦截会报错</li>
            <li><b>construct(target, args) </b>用于拦截new命令；接受两个参数，目标对象和构造函数的参数对象；construct方法返回的必须是一个对象，否则会报错</li>
            <li><b>deleteProperty(target, propKey) </b>拦截delete操作，返回布尔值 如果这个方法抛出错误或者返回false，当前属性就无法被delete命令删除；目标对象自身的不可配置（configurable）的属性，不能被deleteProperty方法删除，否则报错</li>
            <li><b>defineProperty(target, propKey, propDesc) </b>拦截了Object.defineProperty操作，返回一个布尔值；如果目标对象不可扩展（non-extensible），则defineProperty不能增加目标对象上不存在的属性，否则会报错。另外，如果目标对象的某个属性不可写（writable）或不可配置（configurable），则defineProperty方法不得改变这两个设置</li>
            <li><b>getOwnPropertyDescriptor(target, propKey) </b>拦截Object.getOwnPropertyDescriptor()，返回一个属性描述对象或者undefined</li>
            <li><b>getPrototypeOf(target) </b>拦截获取对象原型；getPrototypeOf方法的返回值必须是对象或者null，否则报错；如果目标对象不可扩展（non-extensible）， getPrototypeOf方法必须返回目标对象的原型对象</li>
            <li><b>isExtensible(target) </b>拦截Object.isExtensible操作；只能返回布尔值，否则返回值会被自动转为布尔值；它的返回值必须与目标对象的isExtensible属性保持一致，否则就会抛出错误</li>
            <li><b>ownKeys(target) </b>拦截对象自身属性的读取操作；（使用Object.keys方法时，有三类属性：目标对象上不存在的属性、属性名为 Symbol 值、不可遍历（enumerable）的属性 会被ownKeys方法自动过滤，不会返回）；ownKeys方法返回的数组成员，只能是字符串或 Symbol 值；如果目标对象自身包含不可配置的属性，则该属性必须被ownKeys方法返回，否则报错。；如果目标对象是不可扩展的（non-extensible），这时ownKeys方法返回的数组之中，必须包含原对象的所有属性，且不能包含多余的属性，否则报错</li>
            <li><b>preventExtensions(target) </b>拦截Object.preventExtensions()；必须返回一个布尔值，否则会被自动转为布尔值；只有目标对象不可扩展时（即Object.isExtensible(proxy)为false），proxy.preventExtensions才能返回true，否则会报错，所以通常要在proxy.preventExtensions方法里面，调用一次Object.preventExtensions</li>
            <li><b>setPrototypeOf(target, proto) </b>拦截Object.setPrototypeOf方法；该方法只能返回布尔值，否则会被自动转为布尔值。另外，如果目标对象不可扩展（non-extensible），setPrototypeOf方法不得改变目标对象的原型</li>
        </ul>
        <h4>- Proxy.revocable(): 返回一个对象，该对象的proxy属性是Proxy实例，revoke属性是一个函数，可以取消Proxy实例，当执行revoke函数之后，再访问Proxy实例，就会抛出一个错误</h4>
        <p>Proxy.revocable的一个使用场景是，目标对象不允许直接访问，必须通过代理访问，一旦访问结束，就收回代理权，不允许再次访问</p>
        <pre>
            let target = {};
            let handler = {};

            let {proxy, revoke} = Proxy.revocable(target, handler);

            proxy.foo = 123;
            proxy.foo // 123

            revoke();
            proxy.foo // TypeError: Revoked
        </pre>
        <h4>- this 问题</h4>
        <p>Proxy 代理的情况下，目标对象内部的this关键字会指向 Proxy 代理；有些原生对象的内部属性，只有通过正确的this才能拿到，需要bind绑定正确的this才能代理</p>
    </div>
</template>

<script>
export default {
    mounted() {
        let preObj = {}
        let proxy = new Proxy(preObj, {
            get: (target, propKey) => {
                return 35
            },
            set: (target, propKey, value) => {
                target[propKey] = value + 'b'
                return true
            }
        })
        proxy.demo = 'a'
        console.log(proxy.demo)
        console.log(preObj.demo)
    },
}
</script>

<style scoped>

</style>